#pragma once
#include<pthread.h>
#include<queue>
#include<string>

namespace bit
{
    const int default_size = 5;
    //阻塞队列里的数据应该是让用户来指定的：
    template<class T>
    class blockQueue
    {
    private:
        bool _isFull()
        {
            return _blockqueue.size() == _maxsize;
        }
    public:
        //阻塞队列的构造函数：
        blockQueue(int s = default_size)
        {
            _maxsize = s;
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_ccond, nullptr);
            pthread_cond_init(&_pcond, nullptr);
        }
        //取数据：
        void pop(T* out)
        {
            //消费和生产要互斥，需要用同一把锁加锁：
            pthread_mutex_lock(&_mutex);
            while(_blockqueue.empty())//用while循环来判断，以增强代码的鲁棒性！
            {
                //如果队列为空，消费者不能消费，需要等待：
                pthread_cond_wait(&_ccond, &_mutex);
            }
            //1.阻塞队列没空 || 2.被唤醒啦：
            *out = _blockqueue.front();
            _blockqueue.pop();
            pthread_mutex_unlock(&_mutex);

            //唤醒生产者生产数据：
            if(_blockqueue.size() <= _low_water)
                pthread_cond_signal(&_pcond);
        }
        //存数据：
        void push(const T& in)
        {
            pthread_mutex_lock(&_mutex);
            while(_isFull())
            {
                //如果队列满了，生产者不能生产，需要等待：
                pthread_cond_wait(&_pcond, &_mutex);
            }
            //1.阻塞队列没满 || 2.被唤醒啦：
            _blockqueue.push(in);
            pthread_mutex_unlock(&_mutex);

            //唤醒消费者消费数据：
            if(_blockqueue.size() >= _high_water)
                pthread_cond_signal(&_ccond);
        }
        ~blockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_ccond);
            pthread_cond_destroy(&_pcond);
        }

        //阻塞队列的析构函数：
    private:
        std::queue<T> _blockqueue; //阻塞队列，也是共享资源（临界资源），意味着一切访问阻塞队列的操作必须是原子的！
        pthread_mutex_t _mutex;    //互斥锁；
        
        //条件变量：队列满就让消费者来消费，队列空就让生产者来生产
        pthread_cond_t _ccond;     //消费者的条件变量
        pthread_cond_t _pcond;     //生产者的条件变量

        size_t _maxsize;           //队列的最大容量

        int _low_water = _maxsize / 5;      //低水位线：达到该值表示数据过少，需要生产数据；
        int _high_water = _maxsize / 5 * 4; //高水位线：达到该值表示数据过多，需要消费数据；
    };
}